/*
 * Copyright (c) 2010, okazoh_tk. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *  - Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 *  - Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *  - Neither the name of the copyright holder nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#include "alkes/gfxfilter/GfxFilterGraphics.h"
//#include "alkes/gfxfilter/GfxFilterRenderTarget.h"
#include "alkes/gfxfilter/GfxFilterVertexBuffer.h"
#include "alkes/gfxfilter/GfxFilterDecoderFactory.h"
#include "alkes/gfxfilter/GfxFilterImageBufferFactory.h"

AL_GF_EXPORT_API alkes::IGraphicsDevice* alkes_create_graphics_proxy(alkes::IGraphicsDevice* device, int is_3d)
{
    if (!device) return 0;

    alkes::IGraphicsDevice* filter = 0;
    if (is_3d) {
        filter = alkes::GfxFilterGraphics::create(static_cast<alkes::IGraphics3DDevice*>(device));
    } else {
        filter = alkes::GfxFilterGraphics::create(static_cast<alkes::IGraphics2DDevice*>(device));
    }

    return filter;
}

namespace alkes {

#define BOOL_STR(exp)   ((exp) ? "true" : "false")

#define IS_ENABLE_FILTER(classname, method) \
    this->isEnableLogFilter(AL_FILTER_##classname##method)

#define VALIDATE3D(retval)  \
    if (!is_3d_) { return retval; } \
    IGraphics3DDevice* component(static_cast<IGraphics3DDevice*>(component_))

#define VALIDATE3DN() \
    if (!is_3d_) { return ; } \
    IGraphics3DDevice* component(static_cast<IGraphics3DDevice*>(component_))

GfxFilterGraphics* GfxFilterGraphics::create(IGraphics2DDevice* component)
{
    if (!component) return 0;

    return new GfxFilterGraphics(component);
}

GfxFilterGraphics* GfxFilterGraphics::create(IGraphics3DDevice* component)
{
    if (!component) return 0;

    return new GfxFilterGraphics(component);
}

void GfxFilterGraphics::destroy()
{
    delete this;
}

GfxFilterGraphics::GfxFilterGraphics(IGraphics2DDevice* component)
: component_(static_cast<IGraphics3DDevice*>(component))
, is_3d_(false)
{
}

GfxFilterGraphics::GfxFilterGraphics(IGraphics3DDevice* component)
: component_(component)
, is_3d_(true)
{
    enableAllLogFilter();
}

GfxFilterGraphics::~GfxFilterGraphics()
{
}


#define MAKE_BIT(v) ((uint32_t)((uint32_t)1 << ((v)%32)))
#define INDEX(v)    ((v) / 32)
#define ARRAY_LEN(a)    sizeof(a)/sizeof(a[0])

void GfxFilterGraphics::enableAllLogFilter()
{
    int i;
    for (i = 0; i < static_cast<int>(ARRAY_LEN(log_filter_)); ++i)
    {
        log_filter_[i] = 0xffffffff;
    }
}

void GfxFilterGraphics::disableAllLogFilter()
{
    int i;
    for (i = 0; i < static_cast<int>(ARRAY_LEN(log_filter_)); ++i)
    {
        log_filter_[i] = 0;
    }
}

void GfxFilterGraphics::enableLogFilter(LogFilter filter)
{
    log_filter_[INDEX(filter)] |= MAKE_BIT(filter);
}

void GfxFilterGraphics::disableLogFilter(LogFilter filter)
{
    log_filter_[INDEX(filter)] &= ~MAKE_BIT(filter);
}

bool GfxFilterGraphics::isEnableLogFilter(LogFilter filter) const
{
   return 0 != (log_filter_[INDEX(filter)] & MAKE_BIT(filter));
}


void GfxFilterGraphics::resize(uint16_t width, uint16_t height)
{
    if (IS_ENABLE_FILTER(IGraphicsDevice, resize))
    {
        AL_LOG_INVOKED(IGraphicsDevice, resize, "%d,%d", width, height);
    }
    component_->resize(width, height);
}

void GfxFilterGraphics::flip()
{
    if (IS_ENABLE_FILTER(IGraphicsDevice, flip))
    {
        AL_LOG_INVOKED0(IGraphicsDevice, flip, "");
    }
    component_->flip();
}

RenderTarget* GfxFilterGraphics::createDefaultRenderTarget()
{
    if (IS_ENABLE_FILTER(IGraphics2DDevice, createDefaultRenderTarget))
    {
        AL_LOG_INVOKED0(IGraphics2DDevice, createDefaultRenderTarget, "");
    }

    RenderTarget* ret(component_->createDefaultRenderTarget());

    if (IS_ENABLE_FILTER(IGraphics2DDevice, createDefaultRenderTarget))
    {
        AL_LOG_RETVAL(RenderTarget*, "0x%p", ret);
    }

    return ret;
}

IVertexBuffer* GfxFilterGraphics::createVertexBuffer(uint32_t vertex_type)
{
    if (IS_ENABLE_FILTER(IGraphics3DDevice, createVertexBuffer))
    {
        AL_LOG_INVOKED(IGraphics3DDevice, createVertexBuffer, "0x%08x", vertex_type);
    }

    intrusive_ptr<IVertexBuffer> vbuffer(component_->createVertexBuffer(vertex_type));

    if (!vbuffer)
        return 0;

    GfxFilterVertexBuffer* ret = GfxFilterVertexBuffer::createVertexBuffer(vbuffer);

    if (IS_ENABLE_FILTER(IGraphics3DDevice, createVertexBuffer))
    {
        AL_LOG_RETVAL(IVertexBuffer*, "0x%p", AL_REAL_PTR(ret, GfxFilterVertexBuffer));
    }

    return ret;
}

DecoderFactory* GfxFilterGraphics::createDecoderFactory()
{
    if (IS_ENABLE_FILTER(IGraphicsDevice, createDecoderFactory))
    {
        AL_LOG_INVOKED0(IGraphicsDevice, createDecoderFactory, "");
    }

    DecoderFactory* ret(component_->createDecoderFactory());

    if (IS_ENABLE_FILTER(IGraphicsDevice, createDecoderFactory))
    {
        AL_LOG_RETVAL(DecoderFactory*, "0x%p", AL_REAL_PTR(ret, GfxFilterDecoderFactory));
    }

    return ret;
}

ImageBufferFactory* GfxFilterGraphics::createImageBufferFactory()
{
    if (IS_ENABLE_FILTER(IGraphicsDevice, createImageBufferFactory))
    {
        AL_LOG_INVOKED0(IGraphicsDevice, createImageBufferFactory, "");
    }

    ImageBufferFactory* ret(component_->createImageBufferFactory());

    if (IS_ENABLE_FILTER(IGraphicsDevice, createImageBufferFactory))
    {
        AL_LOG_RETVAL(ImageBufferFactory*, "0x%p", AL_REAL_PTR(ret, GfxFilterImageBufferFactory));
    }

    return ret;
}

Shape* GfxFilterGraphics::createShape(int32_t shape_type, uint32_t num)
{
    if (IS_ENABLE_FILTER(IGraphics2DDevice, createShape))
    {
        AL_LOG_INVOKED(IGraphics2DDevice, createShape, "(ShapeType)%d, %d", shape_type, num);
    }

    Shape* ret(component_->createShape(shape_type, num));

    if (IS_ENABLE_FILTER(IGraphics2DDevice, createShape))
    {
        AL_LOG_RETVAL(Shape*, "0x%p", ret);
    }

    return ret;
}

LineShape* GfxFilterGraphics::createLineShape(uint32_t num)
{
    if (IS_ENABLE_FILTER(IGraphics2DDevice, createLineShape))
    {
        AL_LOG_INVOKED(IGraphics2DDevice, createLineShape, "%d", num);
    }

    LineShape* ret(component_->createLineShape(num));

    if (IS_ENABLE_FILTER(IGraphics2DDevice, createLineShape))
    {
        AL_LOG_RETVAL(LineShape*, "0x%p", ret);
    }

    return ret;
}

RectangleShape* GfxFilterGraphics::createRectangleShape(uint32_t num)
{
    if (IS_ENABLE_FILTER(IGraphics2DDevice, createRectangleShape))
    {
        AL_LOG_INVOKED(IGraphics2DDevice, createRectangleShape, "%d", num);
    }

    RectangleShape* ret(component_->createRectangleShape(num));

    if (IS_ENABLE_FILTER(IGraphics2DDevice, createRectangleShape))
    {
        AL_LOG_RETVAL(RectangleShape*, "0x%p", ret);
    }

    return ret;
}

ImageShape* GfxFilterGraphics::createImageShape(uint32_t num)
{
    if (IS_ENABLE_FILTER(IGraphics2DDevice, createImageShape))
    {
        AL_LOG_INVOKED(IGraphics2DDevice, createImageShape, "%d", num);
    }

    ImageShape* ret(component_->createImageShape(num));

    if (IS_ENABLE_FILTER(IGraphics2DDevice, createImageShape))
    {
        AL_LOG_RETVAL(ImageShape*, "0x%p", ret);
    }

    return ret;
}

PolylineShape* GfxFilterGraphics::createPolylineShape(uint32_t num)
{
    if (IS_ENABLE_FILTER(IGraphics2DDevice, createPolylineShape))
    {
        AL_LOG_INVOKED(IGraphics2DDevice, createPolylineShape, "%d", num);
    }

    PolylineShape* ret(component_->createPolylineShape(num));

    if (IS_ENABLE_FILTER(IGraphics2DDevice, createPolylineShape))
    {
        AL_LOG_RETVAL(PolylineShape*, "0x%p", ret);
    }

    return ret;
}




void GfxFilterGraphics::beginDraw2D(RenderTarget* target)
{
    if (IS_ENABLE_FILTER(IGraphics2DDevice, beginDraw2D))
    {
        AL_LOG_INVOKED(IGraphics2DDevice, beginDraw2D, "(RenderTarget*)0x%p", target);
    }
    component_->beginDraw2D(target);
}

void GfxFilterGraphics::endDraw2D()
{
    if (IS_ENABLE_FILTER(IGraphics2DDevice, endDraw2D))
    {
        AL_LOG_INVOKED0(IRenderTarget2D, endDraw2D, "");
    }
    component_->endDraw2D();
}

const Rect GfxFilterGraphics::getTargetRect() const
{
    return component_->getTargetRect();
}

RenderTarget* GfxFilterGraphics::getRenderTarget()
{
    return component_->getRenderTarget();
}

void GfxFilterGraphics::setScissorRect(const Rect& rect)
{
    if (IS_ENABLE_FILTER(IGraphics2DDevice, setScissorRect))
    {
        AL_LOG_INVOKED(IGraphics2DDevice, setScissorRect, "Rect(%d,%d,%d,%d)", rect.x, rect.y, rect.width, rect.height);
    }
    component_->setScissorRect(rect);
}

const Rect GfxFilterGraphics::getScissorRect() const
{
    return component_->getScissorRect();
}

void GfxFilterGraphics::setState(int state, bool enable)
{
    if (IS_ENABLE_FILTER(IGraphics2DDevice, setState))
    {
        AL_LOG_INVOKED(IGraphics2DDevice, setState, "%d,%s", state, BOOL_STR(enable));
    }
    component_->setState(state, enable);
}

bool GfxFilterGraphics::getState(int state)
{
    return component_->getState(state);
}

void GfxFilterGraphics::setAlphaBlendMode(AlphaBlendMode src_blend, AlphaBlendMode dst_blend)
{
    if (IS_ENABLE_FILTER(IGraphics2DDevice, setAlphaBlendMode))
    {
        AL_LOG_INVOKED(IGraphics2DDevice, setAlphaBlendMode, "(AlphaBlendMode)%d, (AlphaBlendMode)%d", src_blend, dst_blend);
    }
    component_->setAlphaBlendMode(src_blend, dst_blend);
}

void GfxFilterGraphics::getAlphaBlendMode(AlphaBlendMode* src_blend, AlphaBlendMode* dst_blend) const
{
    component_->getAlphaBlendMode(src_blend, dst_blend);
}

void GfxFilterGraphics::setTextureFilter(TexFilterValue min_filter, TexFilterValue mag_filter)
{
    if (IS_ENABLE_FILTER(IGraphics2DDevice, setTextureFilter))
    {
        AL_LOG_INVOKED(IGraphics2DDevice, setTextureFilter, "(TexFilterValue)%d, (TexFilterValue)%d", min_filter, mag_filter);
    }
    component_->setTextureFilter(min_filter, mag_filter);
}

void GfxFilterGraphics::getTextureFilter(TexFilterValue* min_filter, TexFilterValue* mag_filter)
{
    component_->getTextureFilter(min_filter, mag_filter);
}

void GfxFilterGraphics::setTextureAddressMode(TexAddressMode u_mode, TexAddressMode v_mode)
{
    if (IS_ENABLE_FILTER(IGraphics2DDevice, setTextureAddressMode))
    {
        AL_LOG_INVOKED(IGraphics2DDevice, setTextureAddressMode, "(TexAddressMode)%d, (TexAddressMode)%d", u_mode, v_mode);
    }
    component_->setTextureAddressMode(u_mode, v_mode);
}

void GfxFilterGraphics::getTextureAddressMode(TexAddressMode* u_mode, TexAddressMode* v_mode)
{
    component_->getTextureAddressMode(u_mode, v_mode);
}

void GfxFilterGraphics::fill(const Color& color)
{
    if (IS_ENABLE_FILTER(IGraphics2DDevice, fill))
    {
        AL_LOG_INVOKED(IGraphics2DDevice, fill, "Color(%d,%d,%d,%d)",
            color.r, color.g, color.b, color.a);
    }

    component_->fill(color);
}

bool GfxFilterGraphics::drawShape(Shape* shape, uint32_t start, uint32_t count)
{
    if (IS_ENABLE_FILTER(IGraphics2DDevice, drawShape))
    {
        AL_LOG_INVOKED(IGraphics2DDevice, drawShape, "0x%p, %d, %d",
            shape, start, count);
    }
    return component_->drawShape(shape, start, count);
}

bool GfxFilterGraphics::drawShape(LineShape* line_shape, uint32_t start, uint32_t count)
{
    if (IS_ENABLE_FILTER(IGraphics2DDevice, drawShape))
    {
        AL_LOG_INVOKED(IGraphics2DDevice, drawShape, "(LineShape*)0x%p, %d, %d",
            line_shape, start, count);
    }
    return component_->drawShape(line_shape, start, count);
}

bool GfxFilterGraphics::drawShape(RectangleShape* rectangle_shape, uint32_t start, uint32_t count)
{
    if (IS_ENABLE_FILTER(IGraphics2DDevice, drawShape))
    {
        AL_LOG_INVOKED(IGraphics2DDevice, drawShape, "(RectangleShape*)0x%p, %d, %d",
            rectangle_shape, start, count);
    }
    return component_->drawShape(rectangle_shape, start, count);
}

bool GfxFilterGraphics::drawShape(ImageShape* image_shape, uint32_t start, uint32_t count)
{
    if (IS_ENABLE_FILTER(IGraphics2DDevice, drawShape))
    {
        AL_LOG_INVOKED(IGraphics2DDevice, drawShape, "(ImageShape*)0x%p, %d, %d",
            image_shape, start, count);
    }
    return component_->drawShape(image_shape, start, count);
}

bool GfxFilterGraphics::drawShape(PolylineShape* polyline_shape, uint32_t start, uint32_t count)
{
    if (IS_ENABLE_FILTER(IGraphics2DDevice, drawShape))
    {
        AL_LOG_INVOKED(IGraphics2DDevice, drawShape, "(PolylineShape*)0x%p, %d, %d",
            polyline_shape, start, count);
    }
    return component_->drawShape(polyline_shape, start, count);
}

void GfxFilterGraphics::pushMatrix()
{
    if (IS_ENABLE_FILTER(IGraphics2DDevice, pushMatrix))
    {
        AL_LOG_INVOKED0(IGraphics2DDevice, pushMatrix, "");
    }
    component_->pushMatrix();
}

void GfxFilterGraphics::popMatrix()
{
    if (IS_ENABLE_FILTER(IGraphics2DDevice, popMatrix))
    {
        AL_LOG_INVOKED0(IGraphics2DDevice, popMatrix, "");
    }
    component_->popMatrix();
}

void GfxFilterGraphics::translate(const Vector2D& offset)
{
    if (IS_ENABLE_FILTER(IGraphics2DDevice, translate))
    {
        AL_LOG_INVOKED(IGraphics2DDevice, translate, "Vector2D(%f,%f)", offset.x, offset.y);
    }
    component_->translate(offset);
}

void GfxFilterGraphics::translate(float x, float y)
{
    if (IS_ENABLE_FILTER(IGraphics2DDevice, translate))
    {
        AL_LOG_INVOKED(IGraphics2DDevice, translate, "%f, %f", x, y);
    }
    component_->translate(x, y);
}

void GfxFilterGraphics::translateForImage(float x, float y)
{
    if (IS_ENABLE_FILTER(IGraphics2DDevice, translateForImage))
    {
        AL_LOG_INVOKED(IGraphics2DDevice, translateForImage, "%f, %f", x, y);
    }
    component_->translateForImage(x, y);
}

void GfxFilterGraphics::scale(float xscale, float yscale, const Vector2D& origin)
{
    if (IS_ENABLE_FILTER(IGraphics2DDevice, scale))
    {
        AL_LOG_INVOKED(IGraphics2DDevice, scale, "%f, %f, Vector2D(%f,%f)", xscale, yscale, origin.x, origin.y);
    }
    component_->scale(xscale, yscale, origin);
}

void GfxFilterGraphics::scale(float scaling, const Vector2D& origin)
{
    if (IS_ENABLE_FILTER(IGraphics2DDevice, scale))
    {
        AL_LOG_INVOKED(IGraphics2DDevice, scale, "%f, Vector2D(%f,%f)", scaling, origin.x, origin.y);
    }
    component_->scale(scaling, origin);
}

void GfxFilterGraphics::rotate(float angle, const Vector2D& origin)
{
    if (IS_ENABLE_FILTER(IGraphics2DDevice, rotate))
    {
        AL_LOG_INVOKED(IGraphics2DDevice, rotate, "%f, Vector2D(%f,%f)", angle, origin.x, origin.y);
    }
    component_->rotate(angle, origin);
}

void GfxFilterGraphics::skewX(float angle)
{
    if (IS_ENABLE_FILTER(IGraphics2DDevice, skewX))
    {
        AL_LOG_INVOKED(IGraphics2DDevice, skewX, "%f", angle);
    }
    component_->skewX(angle);
}

void GfxFilterGraphics::skewY(float angle)
{
    if (IS_ENABLE_FILTER(IGraphics2DDevice, skewY))
    {
        AL_LOG_INVOKED(IGraphics2DDevice, skewY, "%f", angle);
    }
    component_->skewY(angle);
}

void GfxFilterGraphics::onSnapshot(ImageWriter& writer, const Rect& rect)
{
    component_->snapshot(writer, &rect);
}

/*
void GfxFilterGraphics::beginDraw3D()
{
    if (IS_ENABLE_FILTER(IRenderTarget3D, beginDraw3D))
    {
        AL_LOG_INVOKED(IRenderTarget3D, beginDraw3D, "");
    }

    IRenderTarget3D* target = get3D();
    if (!target)
    {
        return ;
    }

    target->beginDraw3D();
}

void GfxFilterGraphics::endDraw3D()
{
    if (IS_ENABLE_FILTER(IRenderTarget3D, endDraw3D))
    {
        AL_LOG_INVOKED(IRenderTarget3D, endDraw3D, "");
    }

    IRenderTarget3D* target = get3D();
    if (!target)
    {
        return ;
    }
    target->endDraw3D();
}*/

void GfxFilterGraphics::setViewport(const Viewport& viewport)
{
    if (IS_ENABLE_FILTER(IGraphics3DDevice, setViewport))
    {
        AL_LOG_INVOKED(IGraphics3DDevice, setViewport, "Viewport(%d,%d,%d,%d)", viewport.x, viewport.y, viewport.w, viewport.h);
    }

    VALIDATE3DN();
    component->setViewport(viewport);
}

const Viewport GfxFilterGraphics::getViewport() const
{
    VALIDATE3D(UViewport(Rect::ZERO));
    return component->getViewport();
}

void GfxFilterGraphics::setPerspective(float angle, float aspect, float znear, float zfar)
{
    if (IS_ENABLE_FILTER(IGraphics3DDevice, setPerspective))
    {
        AL_LOG_INVOKED(IGraphics3DDevice, setPerspective, "Perspective(%f,%f,%f,%f)", angle, aspect, znear, zfar);
    }

    VALIDATE3DN();
    component->setPerspective(angle, aspect, znear, zfar);
}

void GfxFilterGraphics::setLookAt(const Vector3D& eye, const Vector3D& at, const Vector3D& up)
{
    if (IS_ENABLE_FILTER(IGraphics3DDevice, setLookAt))
    {
        AL_LOG_INVOKED0(IGraphics3DDevice, setLookAt, "LookAt()");
    }

    VALIDATE3DN();
    component->setLookAt(eye, at, up);
}

void GfxFilterGraphics::translate(const Vector3D& offset)
{
    if (IS_ENABLE_FILTER(IGraphics3DDevice, translate))
    {
        AL_LOG_INVOKED(IGraphics3DDevice, translate, "translate(%f,%f,%f)", offset.x, offset.y, offset.z);
    }

    VALIDATE3DN();
    component->translate(offset);
}

void GfxFilterGraphics::translate(float x, float y, float z)
{
    if (IS_ENABLE_FILTER(IGraphics3DDevice, translate))
    {
        AL_LOG_INVOKED(IGraphics3DDevice, translate, "translate(%f,%f,%f)", x, y, z);
    }

    VALIDATE3DN();
    component->translate(x, y, z);
}

void GfxFilterGraphics::scale(float xscale, float yscale, float zscale)
{
    if (IS_ENABLE_FILTER(IGraphics3DDevice, scale))
    {
        AL_LOG_INVOKED(IGraphics3DDevice, scale, "scale(%f,%f,%f)", xscale, yscale, zscale);
    }

    VALIDATE3DN();
    component->scale(xscale, yscale, zscale);
}

void GfxFilterGraphics::rotate(float angle, float x, float y, float z)
{
    if (IS_ENABLE_FILTER(IGraphics3DDevice, rotate))
    {
        AL_LOG_INVOKED(IGraphics3DDevice, rotate, "rotate(%f,%f,%f,%f)", angle, x, y, z);
    }

    VALIDATE3DN();
    component->rotate(angle, x, y, z);
}

void GfxFilterGraphics::clearBuffer(uint32_t buffer_flag, const Color& clear_color, float depth)
{
    if (IS_ENABLE_FILTER(IGraphics3DDevice, clearBuffer))
    {
        AL_LOG_INVOKED(IGraphics3DDevice, clearBuffer, "0x%08x, Color(%d,%d,%d,%d), %f",
            clear_color.r, clear_color.g, clear_color.b, clear_color.a,
            buffer_flag, depth);
    }

    VALIDATE3DN();
    component->clearBuffer(buffer_flag, clear_color, depth);
}

bool GfxFilterGraphics::drawPrimitive(IVertexBuffer* vbuffer, PrimitiveType type, uint32_t start, uint32_t count)
{
    if (IS_ENABLE_FILTER(IGraphics3DDevice, drawPrimitive))
    {
        AL_LOG_INVOKED(IGraphics3DDevice, drawPrimitive, "0x%p, (PrimitiveType)type, %d, %d",
            AL_REAL_PTR(vbuffer, GfxFilterVertexBuffer), type, start, count);
    }

    VALIDATE3D(false);
    return component->drawPrimitive(vbuffer, type, start, count);
}

bool GfxFilterGraphics::setTexture(Image* image, int stage)
{
    if (IS_ENABLE_FILTER(IGraphics3DDevice, setTexture))
    {
        if (image)
        {
            uint16_t x, y, w, h;
            image->getOffset(x, y);
            image->getSize(w, h);
            AL_LOG_INVOKED(IGraphics3DDevice, setTexture, "Image(0x%p,%d,%d,%d,%d), %d",
                image->getImageBuffer(), x, y, w, h, stage);
        }
        else
        {
            AL_LOG_INVOKED(IGraphics3DDevice, setTexture, "Image(0x00000000), %d",
                stage);
        }
    }

    VALIDATE3D(false);
    return component->setTexture(image, stage);
}

void GfxFilterGraphics::setTextureFilter(TexFilterValue min_filter, TexFilterValue mag_filter, int stage)
{
    if (IS_ENABLE_FILTER(IGraphics3DDevice, setTextureFilter))
    {
        AL_LOG_INVOKED(IGraphics3DDevice, setTextureFilter, "(TexFilterValue)%d, (TexFilterValue)%d, %d",
            min_filter, mag_filter, stage);
    }

    VALIDATE3DN();
    component->setTextureFilter(min_filter, mag_filter, stage);
}

void GfxFilterGraphics::getTextureFilter(TexFilterValue* min_filter, TexFilterValue* mag_filter, int stage)
{
    VALIDATE3DN();
    component->getTextureFilter(min_filter, mag_filter, stage);
}

void GfxFilterGraphics::setTextureAddressMode(TexAddressMode u_mode, TexAddressMode v_mode, int stage)
{
    if (IS_ENABLE_FILTER(IGraphics3DDevice, setTextureAddressMode))
    {
        AL_LOG_INVOKED(IGraphics3DDevice, setTextureAddressMode, "(TexAddressMode)%d, (TexAddressMode)%d, %d",
            u_mode, v_mode, stage);
    }

    VALIDATE3DN();
    component->setTextureAddressMode(u_mode, v_mode, stage);
}

void GfxFilterGraphics::getTextureAddressMode(TexAddressMode* u_mode, TexAddressMode* v_mode, int stage)
{
    VALIDATE3DN();
    component->getTextureAddressMode(u_mode, v_mode, stage);
}

Shader* GfxFilterGraphics::createVertexShader(const char* source)
{
    VALIDATE3D(0);
    return component->createVertexShader(source);
}

Shader* GfxFilterGraphics::createFragmentShader(const char* source)
{
    VALIDATE3D(0);
    return component->createFragmentShader(source);
}

Program* GfxFilterGraphics::createShaderProgram(Shader* vertex_shader, Shader* fragment_shader)
{
    VALIDATE3D(0);
    return component->createShaderProgram(vertex_shader, fragment_shader);
}

Program* GfxFilterGraphics::createShaderProgram(const char* vertex_shader_source, const char* fragment_shader_source)
{
    VALIDATE3D(0);
    return component->createShaderProgram(vertex_shader_source, fragment_shader_source);
}

bool GfxFilterGraphics::setShaderProgram(Program* shader_program)
{
    VALIDATE3D(false);
    return component->setShaderProgram(shader_program);
}

}
